#include "bsp_ds18b20.h"
#include "delay.h"  // 需要延时函数支持

/**
 * @brief  初始化DS18B20
 * @param  无
 * @retval 无
 */
void bsp_ds18b20_init(void)
{
    GPIO_InitTypeDef GPIO_InitStructure;
    
    // 使能GPIO时钟
    RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOB, ENABLE);
    
    // 配置GPIO为推挽输出
    GPIO_InitStructure.GPIO_Pin = DS18B20_DQ_PIN;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT;
    GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;
    GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;
    GPIO_Init(DS18B20_DQ_PORT, &GPIO_InitStructure);
    
    DS18B20_DQ_HIGH();  // 初始化为高电平
}

/**
 * @brief  复位DS18B20并检测存在
 * @param  无
 * @retval 0: 成功 1: 失败
 */
static uint8_t DS18B20_Reset(void)
{
    uint8_t retry = 0;
    
    GPIO_InitTypeDef GPIO_InitStructure;
    GPIO_InitStructure.GPIO_Pin = DS18B20_DQ_PIN;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT;
    GPIO_Init(DS18B20_DQ_PORT, &GPIO_InitStructure);
    DS18B20_DQ_LOW();    // 拉低总线
    delay_us(500);       // 保持480us以上
    
    DS18B20_DQ_HIGH();   // 释放总线
    delay_us(60);        // 等待60us
    
    GPIO_InitStructure.GPIO_Pin = DS18B20_DQ_PIN;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN;
    GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL;
    GPIO_Init(DS18B20_DQ_PORT, &GPIO_InitStructure);
    // 等待DS18B20回应
    while (DS18B20_DQ_READ() && retry < 200) {
        retry++;
        delay_us(1);
    }
    
    if (retry >= 200)
        return 1;  // 复位失败
    else
        retry = 0;
    
    // 等待回应结束
    while (!DS18B20_DQ_READ() && retry < 240) {
        retry++;
        delay_us(1);
    }
    
    if (retry >= 240)
        return 1;  // 复位失败
    
    return 0;  // 复位成功
}

/**
 * @brief  向DS18B20写入一个位
 * @param  bit: 要写入的位(0或1)
 * @retval 无
 */
void DS18B20_WriteBit(uint8_t bit)
{
    DS18B20_MODE_OUT();  // 输出模式
    DS18B20_DQ_LOW();    // 拉低总线
    delay_us(2);         // 保持2us
    
    if(bit)
        DS18B20_DQ_HIGH();  // 写1
    
    delay_us(60);        // 保持60us
    DS18B20_DQ_HIGH();   // 释放总线
}

/**
 * @brief  从DS18B20读取一个位
 * @param  无
 * @retval 读取到的位值(0或1)
 */
uint8_t DS18B20_ReadBit(void)
{
    uint8_t bit = 0;
    
    DS18B20_MODE_OUT();  // 输出模式
    DS18B20_DQ_LOW();    // 拉低总线
    delay_us(2);         // 保持2us
    
    DS18B20_DQ_HIGH();   // 释放总线
    DS18B20_MODE_IN();   // 输入模式
    delay_us(12);        // 等待12us
    
    if(DS18B20_DQ_READ())
        bit = 1;
    
    delay_us(45);        // 保持45us
    return bit;
}

/**
 * @brief  向DS18B20写入一个字节
 * @param  byte: 要写入的字节
 * @retval 无
 */
void DS18B20_WriteByte(uint8_t byte)
{
    uint8_t i;
    for(i = 0; i < 8; i++)
    {
        DS18B20_WriteBit(byte & (1 << i));
        delay_us(1);
    }
}

/**
 * @brief  从DS18B20读取一个字节
 * @param  无
 * @retval 读取到的字节
 */
uint8_t DS18B20_ReadByte(void)
{
    uint8_t i, byte = 0;
    for(i = 0; i < 8; i++)
    {
        byte |= DS18B20_ReadBit() << i;
        delay_us(1);
    }
    return byte;
}

/**
 * @brief  读取DS18B20测量的温度值
 * @param  无
 * @retval 温度值(单位:℃)
 */
float DS18B20_GetTemperature(void)
{
    uint8_t temp_lsb, temp_msb;
    int16_t temp_value;
    float temperature;
    
    // 发送转换命令
    if (DS18B20_Reset()) {
        return DS18B20_ERROR;
    }
    DS18B20_WriteByte(0xCC);  // 跳过ROM命令
    DS18B20_WriteByte(0x44);  // 转换温度命令
    
    // 等待转换完成
    delay_ms(750);  // DS18B20最大转换时间为750ms
    
    // 读取温度数据
    if (DS18B20_Reset()) {
        return DS18B20_ERROR;
    }
    DS18B20_WriteByte(0xCC);  // 跳过ROM命令
    DS18B20_WriteByte(0xBE);  // 读取温度命令
    
    temp_lsb = DS18B20_ReadByte();  // 读取温度低8位
    temp_msb = DS18B20_ReadByte();  // 读取温度高8位
    
    temp_value = (temp_msb << 8) | temp_lsb;
    
    // 计算温度值，DS18B20的分辨率为0.0625℃
    if(temp_value < 0)  // 负温度处理
    {
        temperature = (~temp_value + 1) * 0.0625f;
        temperature = -temperature;
    }
    else  // 正温度处理
    {
        temperature = temp_value * 0.0625f;
    }
    
    return temperature;
}

